package com.geekydoo.commands;

import generated.ObjectFactory;
import generated.TaskType;

import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

import javax.xml.datatype.XMLGregorianCalendar;

import com.geekydoo.GeekyDooException;
import com.geekydoo.InputOutputHandler;
import com.geekydoo.InputValidation;
import com.geekydoo.XmlCalendarUtil;
import com.rits.cloning.Cloner;

/**
 * The purpose of this class is to support adding tasks with user-specified
 * attributes.
 * 
 * @author Patrick Dutch
 */
public class AddCommand implements Command {

	private static Logger logger = Logger.getLogger("geekydoo");

	// The list of all task objects managed by the program
	private List<TaskType> taskList;

	// List of all tasks as it was before the new task was added
	private static List<TaskType> preExecutionList;

	// Prompts printed by the add command
	private static final String PROMPT_TITLE = "(*)Title: ";
	private static final String PROMPT_DATE = "(*)Date (DD-MM-YYYY or DD-MM): ";
	private static final String PROMPT_TIME = "Time (HH:MM): ";
	private static final String PROMPT_CATEGORY = "Categories (comma delimited): ";
	private static final String PROMPT_DESCRIPTION = "Description: ";
	private static final String PROMPT_DOCUMENT_LINK = "Document Links (blank to terminate): ";

	// Error messages printed by the add command
	private static final String ERROR_INVALID_TITLE = "Please enter a non-empty title!";
	private static final String ERROR_INVALID_DATE = "Please enter a valid date!";
	private static final String ERROR_INVALID_TIME = "Please enter a valid time!";

	// Descriptive name for the empty string
	private static final String EMPTY_STRING = "";

	public void execute(List<TaskType> tasks) throws GeekyDooException {
		taskList = tasks;
		Cloner cloner = new Cloner();
		preExecutionList = cloner.deepClone(taskList);
		ObjectFactory of = new ObjectFactory();
		TaskType task = of.createTaskType();

		addIdToTask(task);
		logger.info("Added ID to task: " + task.getId());
		addTitleToTask(task);
		logger.info("Added title to task: " + task.getTitle());
		addDateToTask(task);
		logger.info("Added date to task: " + task.getDate());
		addTimeToTask(task);
		logger.info("Added time to task: " + task.getTime());
		addCategoryToTask(task);
		logger.info("Added category to task: " + task.getCategory());
		addDescriptionToTask(task);
		logger.info("Added description to task: " + task.getDescription());
		addDocumentLinkToTask(task);
		logger.info("Added document link to task: " + task.getDocumentLinks());

		int oldSize = taskList.size();
		taskList.add(task);
		assert (taskList.size() == oldSize + 1);

		InputOutputHandler.printToConsole("Successfully added new task \"" + task.getTitle() + "\" to list!", true,
				true);

	}

	/**
	 * This method prompts the user for a list of document links and sets the
	 * task's document links list to the user's input.
	 * 
	 * @param task The task to which document links should be added
	 */
	private void addDocumentLinkToTask(TaskType task) {
		List<String> linkList = task.getDocumentLinks();
		String currentInput;
		while (true) {
			currentInput = InputOutputHandler.getInputFromConsole(PROMPT_DOCUMENT_LINK, true);
			if (currentInput.equals(EMPTY_STRING)) {
				break;
			}
			linkList.add(currentInput);
		}

	}

	/**
	 * This method prompts the user for a task description and sets the new
	 * task's description to the user's input.
	 * 
	 * @param task The task to which a description should be added
	 */
	private void addDescriptionToTask(TaskType task) {
		task.setDescription(InputOutputHandler.getInputFromConsole(PROMPT_DESCRIPTION, true));
	}

	/**
	 * This method prompts the user for a task's categories and sets the new
	 * task's categories to the user's input.
	 * 
	 * @param task The task to which the categories should be added
	 */
	private void addCategoryToTask(TaskType task) {
		task.setCategory(InputOutputHandler.getInputFromConsole(PROMPT_CATEGORY, true));
	}

	/**
	 * This method prompts the user for a task's time and sets the new task's
	 * time to the user's input. If the user's input is an empty string, the new
	 * task will not have an associated time.
	 * 
	 * @param task The task to which the time should be added
	 * @throws GeekyDooException If an error occurs while adding the time to a
	 *             task
	 */
	private void addTimeToTask(TaskType task) throws GeekyDooException {
		String timeString = InputOutputHandler.getInputFromConsole(PROMPT_TIME, true);
		while (!InputValidation.isValidTimeString(timeString) && !timeString.equals(EMPTY_STRING)) {
			InputOutputHandler.printToConsole(ERROR_INVALID_TIME, true, false);
			timeString = InputOutputHandler.getInputFromConsole(PROMPT_TIME, true);
		}

		if (!timeString.equals(EMPTY_STRING)) {
			timeString = XmlCalendarUtil.standardizeTimeString(timeString);
			XMLGregorianCalendar time = XmlCalendarUtil.getCalendarFromTimeString(timeString);
			task.setTime(time);
		}
	}

	/**
	 * This method prompts the user for a task's date and sets the new task's
	 * date to the user's input.
	 * 
	 * @param task The task to which the date should be added
	 * @throws GeekyDooException If an error occurs while adding the date to a
	 *             task
	 */
	private void addDateToTask(TaskType task) throws GeekyDooException {
		String dateString = InputOutputHandler.getInputFromConsole(PROMPT_DATE, true);
		while (!InputValidation.isValidDateString(dateString)) {
			InputOutputHandler.printToConsole(ERROR_INVALID_DATE, true, false);
			dateString = InputOutputHandler.getInputFromConsole(PROMPT_DATE, true);
		}

		dateString = XmlCalendarUtil.standardizeDateString(dateString);
		XMLGregorianCalendar date = XmlCalendarUtil.getCalendarFromDateString(dateString);
		task.setDate(date);
	}

	/**
	 * This method prompts the user for a task's title and sets the new task's
	 * title to the user's input.
	 * 
	 * @param task The task to which the title should be added
	 */
	private void addTitleToTask(TaskType task) {
		String title = InputOutputHandler.getInputFromConsole(PROMPT_TITLE, true);
		while (!InputValidation.isValidTitle(title)) {
			InputOutputHandler.printToConsole(ERROR_INVALID_TITLE, true, false);
			title = InputOutputHandler.getInputFromConsole(PROMPT_TITLE, true);
		}
		task.setTitle(title);
	}

	/**
	 * This method determines the highest ID number currently assigned to a task
	 * in the task list and sets the new task's ID to this number plus one.
	 * 
	 * @param task The task to which the ID should be added
	 */
	private void addIdToTask(TaskType task) {
		int highestId = 0;
		Iterator<TaskType> taskIterator = taskList.iterator();

		while (taskIterator.hasNext()) {
			TaskType currentTask = taskIterator.next();

			int currentTaskId = 0;
			try {
				currentTaskId = Integer.parseInt(currentTask.getId());
			}
			catch (NumberFormatException e) {
				currentTaskId = highestId + 1;
			}
			if (currentTaskId > highestId) {
				highestId = currentTaskId;
			}
		}

		task.setId(Integer.toString(highestId + 1));
	}

	public boolean isListModified() {
		return true;
	}

	public List<TaskType> getPreExecutionList() {
		return preExecutionList;
	}

}
